#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>

#if 0

/* 信号在内核中的表示 */
执行信号的处理动作成为信号递达（Delivery），信号从产生到递达之间的状态称为信号未决(Pending)。
进程可以选择阻塞(Block)某个信号。
被阻塞的信号产生时将保持在未决状态，直到进程解除对此信号的阻塞，才执行递达的动作.
注意：阻塞和忽略是不同的，只要信号被阻塞就不会递达，而忽略是在递达之后可选的一种处理动作信号不会丢失，
如果信号被阻塞，只会保持信号未决，但是信号不丢失只要取消阻塞 信号依然会到来.

每个进程都有自己独立的信号队列，子进程在创建时不会继承父进程的信号队列，子进程的信号队列是空的；  
子进程会继承父进程的信号屏蔽字。信号屏蔽字决定了哪些信号应该被进程忽略或阻塞；  
子进程也会继承父进程为特定信号设置的信号处理函数。


①PCB进程控制块中有信号屏蔽状态字(block)，信号未决状态字(pending)还有是否忽略标识
②信号屏蔽状态字(block)：1代表阻塞，0代表不阻塞；
信号未决状态字(pending)：1代表未决，0代表信号递达
③向进程发送SIGINT，内核首先判断信号屏蔽状态字是否阻塞，如果信号屏蔽状态字阻塞，信号未决状态字(pengding)相应位置1；
若阻塞解除，信号未决状态字(pending)相应位置0，表示信号可以递达了。
④block状态字，pending状态都是64bit，分别代表Linux系统中的64个信号。例如SIGINT是2号信号，对应block状态字中的第二位
⑤block状态字用户可以读写，pending状态字用户只能读，这是新号的设计机制。


/* sigprocmask读取或者更改进程的信号屏蔽状态字(block) */

int sigprocmask(int how,const sigset_t *set,sigset * oset);

成功返回0，出错返回-1

如果oset是非空指针，则读取进程的当前信号屏蔽状态字通过oset参数传出，
如果set是非空指针，则更改进程的信号屏蔽状态字，参数how只是如何更改。
如果oset和set都是非空指针，则先将原来的信号屏蔽字备份到oset里，然后根据set和how参数更改信号屏蔽字。

how含义
--SIG_BLOCK    set包含了我们希望添加到当前信号屏蔽字的信号，相当于mask=mask|set(位或运算)
--SIG_UNBLOCK    set包含了我们希望从当前信号屏蔽字中解除阻塞的信号，相当于mask=mask^set(位异或运算)
--SIG_SETMASK    设置当前信号屏蔽字为set所指向的值，相当于mask=set


/* 获取信号未决状态字（pending）信息  */

int sigpending(sigset_t *set);


#endif

void printsigset(const sigset_t *pset)
{
    int i = 0;
    //遍历64个信号，
    for (; i < 64; i++)
    {
        //信号从1开始   判断哪些信号在信号未决状态字中
        if (sigismember(pset, i + 1))
            putchar('1');
        else
            putchar('0');
        //fflush(stdout);
    }
    printf("\n");
}

void catch_signal(int sign)
{
    switch (sign)
    {
    case SIGINT:
        printf("accept SIGINT!\n");
        exit(0);
        break;
    case SIGQUIT:
        printf("accept SIGQUIT!\n");
        //取消信号阻塞
        //定义信号集
        sigset_t uset;
        //清空信号集
        sigemptyset(&uset);
        //将SIGINT信号加入到信号集中
        sigaddset(&uset,SIGINT);
        //进行位异或操作，将信号集uset更新到进程控制块PCB结构中，取消阻塞信号SIGINT
        sigprocmask(SIG_UNBLOCK,&uset,NULL);
        break;
    }
}

int main(int arg, char *args[])
{
    //定义未决信号集(pending)
    sigset_t pset;
    //定义阻塞信号集(block)
    sigset_t bset;
    //清空信号集
    sigemptyset(&bset);
    //将信号SIGINT加入到信号集中
    sigaddset(&bset, SIGINT);
    //注册信号
    if (signal(SIGINT, catch_signal) == SIG_ERR)
    {
        perror("signal error");
        return -1;
    }
    if (signal(SIGQUIT, catch_signal) == SIG_ERR)
    {
        perror("signal error");
        return -1;
    }
    //进行位或操作，将信号集bset更新到进程控制块PCB结构中，阻塞信号SIGINT（即使用户按下ctrl+c，信号也不会递达）
    sigprocmask(SIG_BLOCK, &bset, NULL);
    while (1)
    {
        /*
         * 获取当前信号未决信息，即使在sigprocmask()函数中设置了信号阻塞，
         * 但是如果没有信号的到来，信号未决状态字对应位依然是0
         * 只要有信号到来，并且被阻塞了，信号未决状态字对应位才会是1
         * */
        sigpending(&pset);
        //打印信号未决信息
        printsigset(&pset);
        sleep(2);
    }
    return 0;
}
